#include <stdio.h>
#include "main.h"
#include "at_common.h"
#include "dev_sim7020.h"

#define APN             "cmnbiot1"

#define AT_CMD_AT       "AT"
#define AT_CMD_OK       "OK"
#define AT_CMD_ATE0     "ATE0"
#define AT_CMD_GSN      "AT+GSN"        //get imei
#define AT_CMD_CGMR     "AT+CGMR"       //software version
#define AT_CMD_SET_CENG "AT+CENG=0"
#define AT_CMD_CENG     "AT+CENG?"      //Get cell info
#define AT_CMD_CPIN     "AT+CPIN?"
#define AT_CMD_CSQ      "AT+CSQ"
#define AT_CMD_CGREG    "AT+CGREG?"     //Network Registration Status
#define AT_CMD_CGACT    "AT+CGACT?"     //Check PDP active staus
#define AT_CMD_CPSMS    "AT+CPSMS=0"
#define AT_CMD_CGCONTRDP    "AT+CGCONTRDP"
#define AT_CMD_APN_SET      "AT*MCGDEFCONT=\"IP\","   //STMIOT  cmnbiot    "AT*MCGDEFCONT=\"IP\",\"cmnbiot\""
#define AT_CMD_CFUN0        "AT+CFUN=0"
#define AT_CMD_CFUN1        "AT+CFUN=1"
   
#define AT_CMD_PSM          "AT+CPSMS=1,,,"  //"AT+CPSMS=1,,,\"01111111\",\"00001010\""
#define AT_CMD_PSM_NOW      "AT+CPSMS=1"
#define AT_CMD_PSM_OFF      "AT+CPSMS=0"
#define AT_CMD_PSM_REPORT   "AT+CPSMSTATUS=1"
#define AT_CMD_CPOWD        "AT+CPOWD=1"
#define AT_CMD_CDNSCFG      "AT+CDNSCFG"
#define AT_CMD_CDNSGIP      "AT+CDNSGIP"

#define AT_CMD_CMQNEW       "AT+CMQNEW" 
#define AT_CMD_CMQTTSNEW    "AT+CMQTTSNEW" 
#define AT_CMD_CMQCON       "AT+CMQCON"
#define AT_CMD_CMQSUB       "AT+CMQSUB"
#define AT_CMD_CMQUNSUB     "AT+CMQUNSUB"   
#define AT_CMD_CMQDISCON    "AT+CMQDISCON"
#define AT_CMD_CMQPUB       "AT+CMQPUB"
#define AT_CMD_CMQALICFG    "AT+CMQALICFG"
#define AT_CMD_CMQALICON    "AT+CMQALICON=0,600,1"
#define AT_CMD_CREVHEX      "AT+CREVHEX=1"   //0-raw data  1-hex

#define AT_CMD_CTZU         "AT+CTZU=1" //auto update time
#define AT_CMD_CCLK         "AT+CCLK?"
#define AT_CMD_CURTC        "AT+CURTC=1"

#define AT_RECV_MQTT        "+CMQPUB:"
#define AT_RECV_MQTT_DISCON "+CMQDISCON:"
#define AT_RECV_NOTIFY      "+CSONMI:"
#define AT_RECV_PSM         "+CPSMSTATUS:"



#define SIM7020_PWR_PIN_HIGH()   HAL_GPIO_WritePin(NB_PWR_GPIO_Port,NB_PWR_Pin,GPIO_PIN_SET)
#define SIM7020_PWR_PIN_LOW()  HAL_GPIO_WritePin(NB_PWR_GPIO_Port,NB_PWR_Pin,GPIO_PIN_RESET)
#define SIM7020_STATE_IS_ON()   HAL_GPIO_ReadPin(NB_STA_GPIO_Port,NB_STA_Pin)
#define SIM7020_STATE_IS_OFF()   (!HAL_GPIO_ReadPin(NB_STA_GPIO_Port,NB_STA_Pin))


#define MAX_RECV_BUF_SIZE  300
#define MAX_SEND_BUF_SIZE  300
#define MAX_IMEI_LEN       20
#define MAX_TOPIC_LEN      80

struct sim_control 
{
    char send_buf[MAX_SEND_BUF_SIZE];
    uint16_t send_len;
    char recv_buf[MAX_RECV_BUF_SIZE];
    int recv_len;
    char recv_topic[MAX_TOPIC_LEN];
    uint8_t socket;

}sim_ctl;

struct sim_info_t
{
    char imei[MAX_IMEI_LEN];
    mqtt_recv_callback mqtt_recv_cb;
}sim_info;

static uint8_t sim_state=AT_STATE_DEINITTED;


bool sim7020_atcmd(char *cmd,char *index,bool delimiter,uint16_t timeout)
{
    memset(sim_ctl.recv_buf,0,MAX_RECV_BUF_SIZE);
    sim_ctl.recv_len=MAX_RECV_BUF_SIZE;
    return at_send_wait_reply(  cmd,strlen(cmd),delimiter,
                                index,"ERROR",
                                sim_ctl.recv_buf,&sim_ctl.recv_len,timeout);    
}


static void sim7020_set_pwrkey(uint16_t ms)
{
    SIM7020_PWR_PIN_LOW();
    vTaskDelay(200);
    SIM7020_PWR_PIN_HIGH();
    vTaskDelay(ms);
    SIM7020_PWR_PIN_LOW();
    vTaskDelay(20);
}

bool sim7020_pwr_on()
{
    uint8_t err = 0;
    if (SIM7020_STATE_IS_ON())
        return true;
    while (err < 3)
    {
        err++;
        AT_DEBUG("SIM7020 POWER ON");
        vTaskDelay(200);
        sim7020_set_pwrkey(1000);
        if (SIM7020_STATE_IS_ON())
            return true;
    }
    return false;
}
/**********************************
    @brief: sim7020 power off
    @note:  
**********************************/
bool sim7020_pwr_off()
{
    uint8_t err = 0;
    if (SIM7020_STATE_IS_OFF())
        return true;
    while (err < 3)
    {
        err++;
        AT_DEBUG("SIM7020 POWER OFF");
        vTaskDelay(200);
        sim7020_set_pwrkey(1500);
        vTaskDelay(500);
        if (SIM7020_STATE_IS_OFF())
            return true;
    }
    return false;
}

static bool sim7020_boot()
{
    uint8_t err = 0;
    AT_DEBUG("SIM7020 BOOT");
    if (sim7020_pwr_on() != true)return false;
    while (err < 3)
    {
        err++;
        vTaskDelay(200);
        if (!sim7020_atcmd(AT_CMD_AT,AT_CMD_OK,true,1000))
            continue;
        if (!sim7020_atcmd(AT_CMD_ATE0,AT_CMD_OK,true,1000))
            continue;
        return true;
    }
    return false;
}

static bool sim7020_reboot()
{
    AT_DEBUG("SIM7020 REBOOT");
    if (SIM7020_STATE_IS_ON())
    {
        sim7020_pwr_off();
    }
    return sim7020_boot();
}

static bool sim7020_get_imei()
{
    uint8_t err=0;
    while(err<3)
    {
        err++;
        if (sim7020_atcmd(AT_CMD_GSN,"86",true,1000)== false)return false;
        strcpy(sim_info.imei,sim_ctl.recv_buf);
        DEBUG("imei is :%s",sim_info.imei);
        return true; 
    }
    return false;
}

static bool sim7020_check_sim()
{
    uint8_t err = 0;
    while (err <= 30)
    {
        err++;
        vTaskDelay(200);
        if(sim7020_atcmd(AT_CMD_CPIN,"+CPIN: READY",true,1000)==false)
            continue;
        return true;
    }
    return false;
}


static bool sim7020_check_net()
{
    uint8_t err = 0;
    while (err <= 250)
    {
        err++;
        vTaskDelay(200);
        if (sim7020_atcmd(AT_CMD_CSQ, "+CSQ:",true,1000) == false)
            continue;
        if (sim7020_atcmd(AT_CMD_CGREG,"+CGREG: 0,1",true,1000) == false)
            continue;
        if (sim7020_atcmd(AT_CMD_CGACT,"+CGACT: 1,1",true,1000) == false)
            continue;
        if (sim7020_atcmd(AT_CMD_CGCONTRDP, "+CGCONTRDP:",true,1000) == false)
            continue;
        return true;
    }
    return false;
}

/**********************************
    @brief: set sim7020 apn parameter
    @note:  
**********************************/
static bool sim7020_set_apn()
{
    uint8_t err = 0;
    snprintf(sim_ctl.send_buf, MAX_SEND_BUF_SIZE, "%s\"%s\"", AT_CMD_APN_SET, APN);
    while (err <= 5)
    {
        err++;
        if (sim7020_atcmd(AT_CMD_CFUN0,"OK", true, 15000) == false)
            continue;
        if (sim7020_atcmd(sim_ctl.send_buf,"OK",true,1000) == false)
            continue;
        if (sim7020_atcmd(AT_CMD_CFUN1, "OK", true, 15000) == false)
            continue;
        return true;
    }
    return false;
}

static bool sim7020_get_cell()
{
    uint8_t err = 0;

    while (err <= 3)
    {
        err++;
        if (sim7020_atcmd(AT_CMD_SET_CENG, "OK",true, 2000) == false)
            continue;
        if (sim7020_atcmd(AT_CMD_CENG,  "+CENG", true, 2000)==false)
            continue;
        return  true;
    }
    return false;
}

static bool sim7020_close_psm()
{
    uint8_t err=0;
    while(err<3)
    {
        err++;
        if(sim7020_atcmd(AT_CMD_PSM_OFF,"OK",true,1000)==false)
            continue;
        return true;
    }
    return false;
}


bool sim7020_mqtt_connect(char *server,uint16_t port,char *proKey, char *devName, char *devSecret)
{
    uint8_t err=0;
    
    if(sim_state==AT_STATE_DEINITTED)return false;
    else if(sim_state==AT_STATE_CONNECTED)return true;

    while(err<3)
    {
        err++;
        if(sim7020_atcmd(AT_CMD_CREVHEX,"OK",true,2000)==false)
            continue;
        memset(sim_ctl.send_buf,0,MAX_SEND_BUF_SIZE);
        snprintf(sim_ctl.send_buf, MAX_SEND_BUF_SIZE, "%s=\"%s\",\"%d\",20000,1024", AT_CMD_CMQNEW, server, port);
        if (sim7020_atcmd(sim_ctl.send_buf, "+CMQNEW",true,60000) == false)
            continue;
        sim_ctl.socket=sim_ctl.recv_buf[9]-0x30;

        memset(sim_ctl.send_buf,0,MAX_SEND_BUF_SIZE);
        snprintf(sim_ctl.send_buf, MAX_SEND_BUF_SIZE, "%s=%d,\"%s\",\"%s\",\"%s\"", AT_CMD_CMQALICFG,sim_ctl.socket,proKey, devName,devSecret);
        if (sim7020_atcmd(sim_ctl.send_buf, "OK",true,10000) == false)
            continue;
        if(sim7020_atcmd(AT_CMD_CMQALICON,"OK",true,5000)==false)
            continue;
        
        sim_state=AT_STATE_CONNECTED;
        return true;
    }
    return false;
}

bool sim7020_mqtt_disconnect(void)
{
    uint8_t err=0;
    if(sim_state==AT_STATE_DEINITTED)return false;
    else if(sim_state!=AT_STATE_CONNECTED)return true;

    memset(sim_ctl.send_buf,0,MAX_SEND_BUF_SIZE);
    snprintf(sim_ctl.send_buf, MAX_SEND_BUF_SIZE, "%s=%d", AT_CMD_CMQDISCON, sim_ctl.socket);
    while(err<3)
    {
        err++;
        if(sim7020_atcmd((char *)sim_ctl.send_buf,"OK",true,3000)==false)
            continue;
        sim_state=AT_STATE_DISCONNECTED;
        return true;
    }
    sim_state=AT_STATE_DISCONNECTED;
	return false;
}

bool sim7020_mqtt_publish(const char *topic, const char *message, uint16_t msg_len,uint32_t timeout)
{
    uint8_t err = 0;
    if(sim_state!=AT_STATE_CONNECTED)return false;

    while (err < 3)
    {
        err++;
        snprintf(sim_ctl.send_buf,MAX_SEND_BUF_SIZE,"%s=%d,\"%s\",1,0,0,%d,\"%s\"", AT_CMD_CMQPUB,sim_ctl.socket,topic,msg_len,message); 
        if (sim7020_atcmd(sim_ctl.send_buf, "OK", true, timeout) == 0)
            continue;
        return true;
    }
    return false;
}

bool sim7020_mqtt_subscribe(const char *topic, int timeout)
{

    uint8_t err=0;
    if(sim_state!=AT_STATE_CONNECTED)return false;

	memset(sim_ctl.send_buf, 0, MAX_SEND_BUF_SIZE);
    while(err<3)
    {
        err++;
        snprintf(sim_ctl.send_buf, MAX_SEND_BUF_SIZE, "%s=%d,\"%s\",1", AT_CMD_CMQSUB, sim_ctl.socket, topic);
        if (sim7020_atcmd(sim_ctl.send_buf, "OK", true, timeout) == 0)
            continue;
        return true;    
    }
    return false;
}

bool sim7020_mqtt_unsubscribe(const char *topic)
{
	uint8_t err=0;
    if(sim_state!=AT_STATE_CONNECTED)return false;

    while(err<3)
    {
        err++;
        snprintf(sim_ctl.send_buf,MAX_SEND_BUF_SIZE,"%s=%d,\"%s\"",AT_CMD_CMQUNSUB,sim_ctl.socket,topic);
        if (sim7020_atcmd(sim_ctl.send_buf, "OK", true, 5000) == 0)
            continue;
        return true;
    }
    return false;
}





void sim70020_mqtt_recv_handler(void *arg ,char *data , int len)
{
    int a,b,c;
    //+CMQPUB: 0,"/sys/a181jbK5wkY/alarm0001/thing/model/up_raw_reply",0,0,0,12,"0212345678C8"
    sscanf(data,"%*[^/] %[^\"]   %*[^,],%d,%d,%d,%d   %*[^\"]\"%[^\"]",sim_ctl.recv_topic,&a,&b,&c,&sim_ctl.recv_len,sim_ctl.recv_buf);
    // AT_DEBUG("recv topic is :%s",sim_ctl.recv_topic);
    // AT_DEBUG("recv data is :%s",sim_ctl.recv_buf);
    if(sim_info.mqtt_recv_cb!=NULL)
    {
        sim_info.mqtt_recv_cb(sim_ctl.recv_topic,sim_ctl.recv_buf,sim_ctl.recv_len);
    }
}

void sim70020_mqtt_disconnect_handler(void *arg ,char *data , int len)
{
    sim_state=AT_STATE_DISCONNECTED;
}



bool sim7020_deinit()
{
    sim_state=AT_STATE_DEINITTED;
    sim7020_pwr_off();
    at_parser_deinit();
    at_parser_deinit();
    memset(&sim_ctl,0,sizeof(sim_ctl));
    return true;
}
bool sim7020_init()
{
    uint8_t err=0;
    at_parser_deinit();
    if(at_parser_init()!=true)return false;
    
    while(err<3)
    {
        err++;
        at_register_callback(AT_RECV_MQTT,sim70020_mqtt_recv_handler,NULL);
        at_register_callback(AT_RECV_MQTT_DISCON,sim70020_mqtt_disconnect_handler,NULL);

        memset(&sim_ctl,0,sizeof(sim_ctl));    

        if(sim7020_reboot()!=true)continue;

        if(sim7020_get_imei()!=true)continue;

        if(sim7020_check_sim()!=true)continue;

        if(sim7020_set_apn()!=true)continue;

        if(sim7020_check_net()!=true)continue;

        if(sim7020_get_cell()!=true)continue;

        if(sim7020_close_psm()!=true)continue;

        sim_state=AT_STATE_INITED;
        return true;
    }
    
    return false;
}
bool sim7020_register_mqtt_recv_callback(mqtt_recv_callback cb)
{
    sim_info.mqtt_recv_cb=cb;
    return true;
}

at_dev_t dev_sim7020={
    .init=sim7020_init,
    .deinit=sim7020_deinit,
    .mqtt_connect=sim7020_mqtt_connect,
    .mqtt_disconnect=sim7020_mqtt_disconnect,
    .mqtt_pub=sim7020_mqtt_publish,
    .mqtt_sub=sim7020_mqtt_subscribe,
    .mqtt_unsub=sim7020_mqtt_unsubscribe,
    .mqtt_recv_register=sim7020_register_mqtt_recv_callback,
};

